//[-------------------------------------------------------]
//[ Includes                                              ]
//[-------------------------------------------------------]

// QT includes

#include "OSMHandler.h"
#include <iostream>

#include "OSMNode.h"
#include "OSMWay.h"
#include "OSMRelation.h"

#include <cassert>

OSMHandler::OSMHandler():QXmlDefaultHandler(), m_state(UNDEFINED)
{
}

OSMHandler::~OSMHandler()
{

}

bool OSMHandler::startDocument()
{
	return true;
}

bool OSMHandler::endDocument()
{
	return true;
}

bool OSMHandler::startElement( const QString& namespaceURI, const QString& localName , const QString& qName, const QXmlAttributes& atts )
{
    //////////////////////////////////////////////////////////////////////////
    // head tags
	if(localName == "node")
	{
        bool ok = parseOSMNode(atts);
        m_state = NODE;
        return ok;
	}

    if(localName == "way")
    {
        bool ok = parseOSMWay(atts);
        m_state = WAY;
        return ok;
    }

    if(localName == "relation")
    {
        bool ok = parseOSMRelation(atts);
        m_state = RELATION;
        return ok;
    }
    //
    //////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////////
    // body tags

    if(m_state == NODE)
    {
        if(localName == "tag")
        {
            bool ok = parseOSMNodeTag(atts);
            return ok;
        }
    }

    if(m_state == WAY)
    {
        if(localName == "nd")
        {
            bool ok = parseOSMWayPoint(atts);
            return ok;
        }

        if(localName == "tag")
        {
            bool ok = parseOSMWayTag(atts);
            return ok;
        }
    }

    if(m_state == RELATION)
    {
        if(localName == "member")
        {
            bool ok = parseOSMRelationMember(atts);
            return ok;
        }

        if(localName == "tag")
        {
            bool ok = parseOSMRelationTag(atts);
        }
    }
    //
    //////////////////////////////////////////////////////////////////////////

	return true;
}

bool OSMHandler::endElement( const QString& namespaceURI, const QString& localName, const QString& qName )
{
	if(localName == "node")
    {
        m_state = UNDEFINED;
    }
	
	return true;
}

bool OSMHandler::parseOSMNode(const QXmlAttributes& atts)
{
    OSMNode node;
    bool foundId = false;
    bool foundLat = false;
    bool foundLon = false;

    for(int i = 0; i < atts.count(); i++)
    {
        if(atts.localName(i) == "id")
        {
            node.setId(atts.value(i).toUInt());
            foundId = true;
        }

        if(atts.localName(i) == "lat")
        {
            node.setLatitude(atts.value(i).toFloat());
            foundLat = true;
        }

        if(atts.localName(i) == "lon")
        {
            node.setLongitude(atts.value(i).toFloat());
            foundLon = true;
        }

        if(foundId && foundLat && foundLon)
        {
            m_osmNodes.push_back(node);
            break;
        }
    }

    if(!(foundId && foundLat && foundLon))
    {
        std::cerr << "***Error***: Incomplete Node found\n";
        return false;
    }

    return true;
}

bool OSMHandler::parseOSMNodeTag(const QXmlAttributes& atts)
{
    OSMNode& node = m_osmNodes.back();
    assert(atts.count() == 2);

    node.addFeature(atts.value(0), atts.value(1));

    return true;
}

bool OSMHandler::parseOSMWayTag(const QXmlAttributes& atts)
{
    OSMWay& way = m_osmWays.back();
    assert(atts.count() == 2);

    way.addFeature(atts.value(0), atts.value(1));
    return true;
}

bool OSMHandler::parseOSMRelationTag(const QXmlAttributes& atts)
{
    OSMRelation& relation = m_osmRelations.back();
    assert(atts.count() == 2);

    relation.addFeature(atts.value(0), atts.value(1));

    return true;
}

bool OSMHandler::parseOSMWay(const QXmlAttributes& atts)
{
    OSMWay way;
    bool foundId = false;

    for(int i = 0; i < atts.count(); ++i)
    {
        if(atts.localName(i) == "id")
        {
            way.setId(atts.value(i).toUInt());
            foundId = true;
            m_osmWays.push_back(way);
            break;
        }
    }

    if(!foundId)
    {
        std::cerr << "***Error***: Incomplete Way found\n";
        return false;
    }

    return true;
}

bool OSMHandler::parseOSMWayPoint(const QXmlAttributes& atts)
{
    bool foundRef = false;

    for(int i = 0; i < atts.count(); ++i)
    {
        if(atts.localName(i) == "ref")
        {
            m_osmWays.back().addNodeId(atts.value(i).toUInt());
            foundRef = true;
            break;
        }
    }

    if(!foundRef)
    {
        std::cerr << "***Error***: Incomplete Way without waypoint reference found\n";
        return false;
    }

    return true;
}

bool OSMHandler::parseOSMRelation(const QXmlAttributes& atts)
{
    OSMRelation relation;
    bool foundId = false;

    for(int i = 0; i < atts.count(); ++i)
    {
        if(atts.localName(i) == "id")
        {
            relation.setId(atts.value(i).toUInt());
            foundId = true;
            m_osmRelations.push_back(relation);
            break;
        }
    }

    if(!foundId)
    {
        std::cerr << "***Error***: Incomplete Relation found\n";
        return false;
    }

    return true;
}

bool OSMHandler::parseOSMRelationMember(const QXmlAttributes& atts)
{
    bool foundNodeOrWayOrRelation = false;

    for(int i = 0; i < atts.count(); ++i)
    {
        if(atts.localName(i) == "type")
        {
            if(atts.value(i) == "node")
            {
                m_osmRelations.back().addNodeId(atts.value(i+1).toUInt());
                foundNodeOrWayOrRelation = true;
                break;
            }

            if(atts.value(i) == "way")
            {
                m_osmRelations.back().addWayId(atts.value(i+1).toUInt(), atts.value(i+2));
                foundNodeOrWayOrRelation = true;
                break;
            }

            if(atts.value(i) == "relation")
            {
                m_osmRelations.back().addRelationId(atts.value(i+1).toUInt());
                foundNodeOrWayOrRelation = true;
                break;
            }
        }
    }

    if(!foundNodeOrWayOrRelation)
    {
        std::cerr << "***Error***: Malformed xml tag <member> in relation\n";
        for(int i = 0; i < atts.count(); ++i)
        {
            std::cerr << atts.localName(i).toStdString() << " " << atts.value(i).toStdString() << std::endl;
        }
        return false;
    }

    return true;
}